/*
 * Flowmeter.cpp
 *
 *  Created on: 2015年7月31日
 *      Author: chuanjiang.zh@qq.com
 */

#include "Flowmeter.h"
#include "ClockTime.h"

Flowmeter::Flowmeter():
    m_window(DEF_WINDOW * 1000),
    m_tmfirst(),
    m_tmlast(),
    m_windowBytes(),
    m_tmStart(),
    m_totalBytes(),
    m_counter(),
    m_lostCount()
{
}

Flowmeter::~Flowmeter()
{
}

int64_t Flowmeter::getClock()
{
    return comn::ClockTime::getTime();
}

void Flowmeter::setWindow(size_t seconds)
{
    if (seconds <= 1)
    {
        return;
    }

    m_window = seconds * 1000;
}

size_t Flowmeter::getWindow()
{
    return m_window / 1000;
}

void Flowmeter::count(int bytes)
{
    if (bytes < 0)
    {
        return;
    }

    comn::AutoCritSec lock(m_cs);
    
    m_counter ++;

    Point pt(bytes, getClock());

    if (m_pointDeque.empty())
    {
        m_tmfirst = pt.second;
        m_tmlast = pt.second;

        m_pointDeque.push_back(pt);

        m_windowBytes = pt.first;
        m_totalBytes = pt.first;
        m_tmStart = pt.second;
    }
    else
    {
        Point ptBack = m_pointDeque.back();
        if (pt.second < ptBack.second)
        {
            /// error
            clear();
        }

        m_tmlast = pt.second;
        m_pointDeque.push_back(pt);
        m_windowBytes += pt.first;
        m_totalBytes += pt.first;

        removeOutdated(pt.second);
    }

}

double Flowmeter::getRate()
{
    comn::AutoCritSec lock(m_cs);

    int64_t clockNow = getClock();
    removeOutdated(clockNow);

    if (m_pointDeque.empty())
    {
        return 0;
    }

    int64_t duration = clockNow - m_tmfirst;
    if (duration <= 0)
    {
        return 0;
    }

    double bytes = (double)m_windowBytes;
    return bytes / duration;
}

double Flowmeter::getAvgRate()
{
    int64_t clockNow = getClock();
    int64_t duration = clockNow - m_tmStart;
    if (duration <= 0)
    {
        return 0;
    }

    double bytes = (double)m_totalBytes;
    return bytes / duration;
}

int64_t Flowmeter::getTotalBytes()
{
    return m_totalBytes;
}

void Flowmeter::clear()
{
    comn::AutoCritSec lock(m_cs);

    m_pointDeque.clear();
    m_tmfirst = getClock();
    m_tmlast = getClock();
    m_windowBytes = 0;
    m_totalBytes = 0;
    m_counter = 0;
    m_lostCount = 0;
}

void Flowmeter::removeOutdated(int64_t clockNow)
{
    int64_t clockRef = clockNow - m_window;

    while (!m_pointDeque.empty())
    {
        Point& ptFront = m_pointDeque.front();
        if (ptFront.second < clockRef)
        {
            m_windowBytes -= ptFront.first;
            m_tmfirst = ptFront.second;

            m_pointDeque.pop_front();
        }
        else
        {
            break;
        }
    }

    if (m_pointDeque.size() > m_window)
    {
        m_pointDeque.clear();
    }

    if (m_pointDeque.empty())
    {
        m_tmfirst = clockNow;
        m_tmlast = clockNow;
        m_windowBytes = 0;
    }
}

int64_t Flowmeter::getCounter()
{
    return m_counter;
}

int64_t Flowmeter::getLostCount()
{
    return m_lostCount;
}

void    Flowmeter::lostPacket(int count)
{
    if (count > 0)
    {
        m_lostCount += count;
    }
    
}
